﻿using System;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using USBXpressNET;

namespace USBXpressNETExample
{
    public partial class Example : Form
    {
        private bool UseUSBDevice { get { return radioButton1.Checked; } }

        public Example()
        {
            InitializeComponent();
        }

        private void buttonNumDevices_Click(object sender, EventArgs e)
        {
            richTextBox1.Clear();
            if (UseUSBDevice)
            {
                richTextBox1.AppendText("Number of Devices: " + USBDevice.GetNumDevices());
            }
            else
            {
                int devices = 0;
                var retVal = API.GetNumDevices(out devices);
                richTextBox1.AppendText("Number of Devices: " + devices);
            }
        }

        private void buttonDeviceInfo_Click(object sender, EventArgs e)
        {
            richTextBox1.Clear();
            if (UseUSBDevice)
            {
                foreach (USBDevice device in USBDevice.DeviceList.Values)
                {
                    richTextBox1.AppendText(string.Format("Serial: {0}\n", device.SerialNumber));
                    richTextBox1.AppendText(string.Format("Part Number: {0}\n", device.PartNumber));
                    richTextBox1.AppendText(string.Format("Link Name: {0}\n", device.LinkName));
                    richTextBox1.AppendText(string.Format("Product ID: {0}\n", device.PID));
                    richTextBox1.AppendText(string.Format("Vendor ID: {0}\n", device.VID));
                    richTextBox1.AppendText(string.Format("Description: {0}\n", device.Description));
                    richTextBox1.AppendText("------------------------------------------------------------------------\n");
                }
            }
            else
            {
                int devices = 0;
                var retVal = API.GetNumDevices(out devices);
                string productString;
                for (int i = 0; i < devices; i++)
                {
                    retVal = API.GetProductString(i, out  productString, GetProductOptions.SI_RETURN_SERIAL_NUMBER);
                    richTextBox1.AppendText(string.Format("Serial: {0}\n", productString));

                    retVal = API.GetProductString(i, out  productString, GetProductOptions.SI_RETURN_LINK_NAME);
                    richTextBox1.AppendText(string.Format("Link Name: {0}\n", productString));

                    retVal = API.GetProductString(i, out  productString, GetProductOptions.SI_RETURN_PID);
                    richTextBox1.AppendText(string.Format("Product ID: {0}\n", productString));

                    retVal = API.GetProductString(i, out  productString, GetProductOptions.SI_RETURN_VID);
                    richTextBox1.AppendText(string.Format("Vendor ID: {0}\n", productString));

                    retVal = API.GetProductString(i, out  productString, GetProductOptions.SI_RETURN_DESCRIPTION);
                    richTextBox1.AppendText(string.Format("Description: {0}\n", productString));
                    richTextBox1.AppendText("------------------------------------------------------------------------\n");
                }

            }
        }

        private void TestForm_Load(object sender, EventArgs e)
        {

        }

        private Thread _loopbackThread;
        private void buttonLoopback_Click(object sender, EventArgs e)
        {
            if(_loopbackThread == null)
            {
                _loopbackThread = UseUSBDevice ? new Thread(LoopbackThreadUSBDevice) : new Thread(LoopbackThreadAPI);
            }
            if(_loopbackThread.ThreadState == ThreadState.Unstarted)
            {
                richTextBox1.Clear();
                richTextBox1.AppendText("Performing Write and Read test\n");
                _loopbackThread.Start();
            }
            else if(_loopbackThread.ThreadState == ThreadState.Stopped)
            {
                _loopbackThread = UseUSBDevice ? new Thread(LoopbackThreadUSBDevice) : new Thread(LoopbackThreadAPI);
                richTextBox1.Clear();
                richTextBox1.AppendText("Performing Write and Read test\n");
                _loopbackThread.Start();
            }
        }

        private void LoopbackThreadAPI()
        {
            int devices = 0;
            var retVal = API.GetNumDevices(out devices);
            uint handle;

            if (devices > 0)
            {
                // Get the first device
                retVal = API.Open(0, out handle);

                byte[] writeBuffer = new byte[512];
                byte[] readBuffer = new byte[512];
                // Fill write buffer with data
                for (int i = 0; i < writeBuffer.Length; i++)
                {
                    writeBuffer[i] = (byte)i;
                }
                // Flush the current read and write buffers to ensure they are empty
                API.FlushBuffers(handle, true, true);
                API.SetTimeouts(5000, 5000);

                // Write the buffer to the device
                Invoke((MethodInvoker)(() => richTextBox1.AppendText("Transmitting ...\n")));

                int count;
                retVal = API.Write(handle, ref writeBuffer, out count, 0);
                if (retVal != ReturnValue.SI_SUCCESS)
                    MessageBox.Show("Failed to write the Device\n" + retVal.Description());

                Thread.Sleep(10000); // wait for device to finish transmiting data
                // if device was in a loopback configuration, then the data should be waiting in the Read buffer
                Invoke((MethodInvoker)(() => richTextBox1.AppendText("Reading ...\n")));

                retVal = API.Read(handle, readBuffer, out count);
                if (retVal != ReturnValue.SI_SUCCESS)
                    MessageBox.Show("Failed to read the Device\n" + retVal.Description());

                API.Close(ref handle);

                Invoke((MethodInvoker)(() => richTextBox1.AppendText("Verifying transmitted data...\n")));
                // Verify the readBuffer == writeBuffer
                int errorCount = 0;
                for (int i = 0; i < count; i++)
                {
                    if (writeBuffer[i] != readBuffer[i])
                    {
                        errorCount++;
                        Invoke(
                            (MethodInvoker)
                            (() =>
                             richTextBox1.AppendText(string.Format("Error at index {0}: expected: {1} actual {2}", i,
                                                                   writeBuffer[i], readBuffer[i]))));
                    }
                }
                if (errorCount == 0) Invoke((MethodInvoker)(() => richTextBox1.AppendText("Test Successful\n")));
                else
                    Invoke(
                        (MethodInvoker)
                        (() => richTextBox1.AppendText(string.Format("{0} mismatches shown above\n", errorCount))));
            }
            else Invoke((MethodInvoker)(() => richTextBox1.AppendText("No devices found\n")));
        }

        private void LoopbackThreadUSBDevice()
        {
            if (USBDevice.DeviceList.Count > 0)
            {
                // Get the first device
                USBDevice device = USBDevice.DeviceList.Values.FirstOrDefault();
                byte[] writeBuffer = new byte[512];
                byte[] readBuffer = new byte[512];
                // Fill write buffer with data
                for (int i = 0; i < writeBuffer.Length; i++)
                {
                    writeBuffer[i] = (byte)i;
                }
                // Flush the current read and write buffers to ensure they are empty
                device.FlushBuffers();
                device.Timeouts = new ReadWriteTimeouts(5000, 5000);
                // Write the buffer to the device
                Invoke((MethodInvoker)(() => richTextBox1.AppendText("Transmitting ...\n")));

                int count = device.Write(writeBuffer);
                Thread.Sleep(10000); // wait for device to finish transmiting data
                // if device was in a loopback configuration, then the data should be waiting in the Read buffer
                Invoke((MethodInvoker)(() => richTextBox1.AppendText("Reading ...\n")));
                count = device.Read(readBuffer);

                Invoke((MethodInvoker)(() => richTextBox1.AppendText("Verifying transmitted data...\n")));
                // Verify the readBuffer == writeBuffer
                int errorCount = 0;
                for (int i = 0; i < count; i++)
                {
                    if (writeBuffer[i] != readBuffer[i])
                    {
                        errorCount++;
                        Invoke(
                            (MethodInvoker)
                            (() =>
                             richTextBox1.AppendText(string.Format("Error at index {0}: expected: {1} actual {2}", i,
                                                                   writeBuffer[i], readBuffer[i]))));
                    }
                }
                if (errorCount == 0) Invoke((MethodInvoker) (() => richTextBox1.AppendText("Test Successful\n")));
                else
                    Invoke(
                        (MethodInvoker)
                        (() => richTextBox1.AppendText(string.Format("{0} mismatches shown above\n", errorCount))));
            }
            else Invoke((MethodInvoker) (() => richTextBox1.AppendText("No devices found\n")));
        }

        private void buttonVersions_Click(object sender, EventArgs e)
        {
            richTextBox1.Clear();
            if (UseUSBDevice)
            {
                richTextBox1.AppendText(string.Format("DLL Version: {0}\n", USBDevice.DLLVersionString));
                richTextBox1.AppendText(string.Format("Driver Version: {0}\n", USBDevice.DriverVersionString));
            }
            else
            {
                int highVersion, lowVersion;
                
                API.GetDLLVersion(out highVersion, out lowVersion);
                long DLLVersion = ((long)highVersion << 32) + lowVersion;
                string version = string.Format("{0:X}.{1:X}.{2:X}.{3:X}", (DLLVersion >> 48) & 0xFFFF, (DLLVersion >> 32) & 0xFFFF,
                     (DLLVersion >> 16) & 0xFFFF, DLLVersion & 0xFFFF);
                richTextBox1.AppendText(string.Format("DLL Version: {0}\n", version));

                API.GetDriverVersion(out highVersion, out lowVersion);
                long DriverVersion = ((long)highVersion << 32) + lowVersion;
                version = string.Format("{0:X}.{1:X}.{2:X}.{3:X}", (DriverVersion >> 48) & 0xFFFF,
                                        (DriverVersion >> 32) & 0xFFFF,
                                        (DriverVersion >> 16) & 0xFFFF, DriverVersion & 0xFFFF);
                richTextBox1.AppendText(string.Format("Driver Version: {0}\n", version));
            }
        }
    }
}

